package com.mdinic.blogovi.model;

import java.io.Serializable;
import java.util.Date;
import java.util.List;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.TypedQuery;
import javax.persistence.Version;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.roo.addon.javabean.RooJavaBean;
import org.springframework.roo.addon.jpa.activerecord.RooJpaActiveRecord;
import org.springframework.transaction.annotation.Transactional;

@Entity
@Configurable
@RooJavaBean
@RooJpaActiveRecord(finders = { "findBlogPostsByApprovedNot", "findBlogPostsByDescriptionEquals",
        "findBlogPostsByHeadlineEquals", "findBlogPostsByUrlEquals", "findBlogPostsByBlog",
        "findBlogPostsByTitleSetNot", "findBlogPostsByPerson", "findBlogPostsByPromotedIsNull",
        "findBlogPostsByPromotedIsNotNull" })
public class BlogPost implements Serializable {

    private static final Logger LOG = Logger.getLogger(BlogPost.class);

    private static final long serialVersionUID = 1L;

    public static final String HEADLINE = "headline";

    public static final String DESCRIPTION = "description";

    public static final String URL = "url";

    public static final String VIEW = "view";

    public static final String CLICK = "click";

    public static final String POSTED = "posted";

    public static final String TYPE = "type";

    public static final String COMMENT = "comment";

    public static final String VOTE_UP = "voteUp";

    public static final String VOTE_DOWN = "voteDown";

    @NotNull
    @Size(min = 5, max = 256)
    private String url;

    private String thumb;

    @NotNull
    @Size(min = 2, max = 256)
    private String headline;

    @NotNull
    @Size(min = 20, max = 500)
    private String description;

    @NotNull
    @ManyToOne
    private BlogType type;

    @NotNull
    private Integer view;

    @NotNull
    private Integer click;

    @NotNull
    private Integer comment;

    @NotNull
    private Integer voteUp;

    @NotNull
    private Integer voteDown;

    @NotNull
    @ManyToOne
    private Person person;

    @NotNull
    private Boolean approved;

    private Integer reported;

    @NotNull
    @Temporal(TemporalType.TIMESTAMP)
    @DateTimeFormat(style = "M-")
    private Date posted;

    @Temporal(TemporalType.TIMESTAMP)
    @DateTimeFormat(style = "M-")
    private Date promoted;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private Long id;

    @Version
    @Column(name = "version")
    private Integer version;

    @PersistenceContext
    transient EntityManager entityManager;

    @NotNull
    @ManyToOne
    private Blog blog;

    public String getUrl() {
        return this.url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getThumb() {
        return this.thumb;
    }

    public void setThumb(String thumb) {
        this.thumb = thumb;
    }

    public String getHeadline() {
        return this.headline;
    }

    public void setHeadline(String headline) {
        this.headline = headline;
    }

    public String getDescription() {
        return this.description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public BlogType getType() {
        return this.type;
    }

    public void setType(BlogType type) {
        this.type = type;
    }

    public Integer getView() {
        return this.view;
    }

    public void setView(Integer view) {
        this.view = view;
    }

    public Integer getClick() {
        return this.click;
    }

    public void setClick(Integer click) {
        this.click = click;
    }

    public Integer getComment() {
        return this.comment;
    }

    public void setComment(Integer comment) {
        this.comment = comment;
    }

    public Integer getVoteUp() {
        return this.voteUp;
    }

    public void setVoteUp(Integer voteUp) {
        this.voteUp = voteUp;
    }

    public Integer getVoteDown() {
        return this.voteDown;
    }

    public void setVoteDown(Integer voteDown) {
        this.voteDown = voteDown;
    }

    public Person getPerson() {
        return this.person;
    }

    public void setPerson(Person person) {
        this.person = person;
    }

    public Date getPosted() {
        return posted;
    }

    public void setPosted(Date posted) {
        this.posted = posted;
    }

    public Date getPromoted() {
        return promoted;
    }

    public void setPromoted(Date promoted) {
        this.promoted = promoted;
    }

    public Boolean getApproved() {
        return approved;
    }

    public void setApproved(Boolean approved) {
        this.approved = approved;
    }

    public Blog getBlog() {
        return this.blog;
    }

    public void setBlog(Blog blog) {
        this.blog = blog;
    }

    public Integer getReported() {
        return reported;
    }

    public void setReported(Integer reported) {
        this.reported = reported;
    }

    public Long getId() {
        return this.id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Integer getVersion() {
        return this.version;
    }

    public void setVersion(Integer version) {
        this.version = version;
    }

    public static final EntityManager entityManager() {
        EntityManager em = new BlogPost().entityManager;
        if (em == null)
            throw new IllegalStateException(
                    "Entity manager has not been injected (is the Spring Aspects JAR configured as an AJC/AJDT aspects library?)");
        return em;
    }

    public static long countBlogPosts() {
        return entityManager().createQuery("SELECT COUNT(o) FROM BlogPost o", Long.class).getSingleResult();
    }

    public static long countBlogPostsByPerson(Person person) {
        TypedQuery<Long> createQuery = entityManager().createQuery(
                "SELECT COUNT(o) FROM BlogPost o WHERE o.person = :person", Long.class);
        createQuery.setParameter("person", person);
        return createQuery.getSingleResult();
    }

    public static List<com.mdinic.blogovi.model.BlogPost> findAllBlogPosts() {
        return entityManager().createQuery("SELECT o FROM BlogPost o", BlogPost.class).getResultList();
    }

    public static List<com.mdinic.blogovi.model.BlogPost> findApprovedBlogPostEntries(int firstResult, int maxResults) {
        return entityManager()
                .createQuery("SELECT o FROM BlogPost o WHERE o.approved IS true ORDER BY o.id DESC", BlogPost.class)
                .setFirstResult(firstResult).setMaxResults(maxResults).getResultList();
    }

    public static List<com.mdinic.blogovi.model.BlogPost> findPromotedApprovedBlogPostEntries(int firstResult,
            int maxResults) {
        return entityManager()
                .createQuery(
                        "SELECT o FROM BlogPost o WHERE o.promoted IS NOT null and o.approved IS true ORDER BY o.id DESC",
                        BlogPost.class).setFirstResult(firstResult).setMaxResults(maxResults).getResultList();
    }

    public static List<com.mdinic.blogovi.model.BlogPost> findPromotedApprovedBlogPostEntries(int firstResult,
            int maxResults, BlogType type) {
        TypedQuery<BlogPost> q = entityManager()
                .createQuery(
                        "SELECT o FROM BlogPost o WHERE o.promoted IS NOT null and o.approved IS true AND o.type = :type ORDER BY o.id DESC",
                        BlogPost.class);
        q.setParameter(BlogPost.TYPE, type);
        q.setFirstResult(firstResult).setMaxResults(maxResults);
        return q.getResultList();
    }

    public static List<com.mdinic.blogovi.model.BlogPost> findNotPromotedApprovedBlogPostEntries(int firstResult,
            int maxResults, BlogType blogType) {
        TypedQuery<BlogPost> setMaxResults = entityManager()
                .createQuery(
                        "SELECT o FROM BlogPost o WHERE o.promoted IS null and o.approved IS true AND o.type = :type ORDER BY o.id DESC",
                        BlogPost.class);
        setMaxResults.setParameter(BlogPost.TYPE, blogType);
        setMaxResults.setFirstResult(firstResult).setMaxResults(maxResults);
        return setMaxResults.getResultList();
    }

    public static List<com.mdinic.blogovi.model.BlogPost> findNotPromotedApprovedBlogPostEntries(int firstResult,
            int maxResults) {
        TypedQuery<BlogPost> setMaxResults = entityManager().createQuery(
                "SELECT o FROM BlogPost o WHERE o.promoted IS null and o.approved IS true ORDER BY o.id DESC",
                BlogPost.class);
        setMaxResults.setFirstResult(firstResult).setMaxResults(maxResults);
        return setMaxResults.getResultList();
    }

    public static com.mdinic.blogovi.model.BlogPost findBlogPost(Long id) {
        if (id == null)
            return null;
        return entityManager().find(BlogPost.class, id);
    }

    public static List<com.mdinic.blogovi.model.BlogPost> findBlogPostEntries(int firstResult, int maxResults) {
        return entityManager().createQuery("SELECT o FROM BlogPost o", BlogPost.class).setFirstResult(firstResult)
                .setMaxResults(maxResults).getResultList();
    }

    public static TypedQuery<com.mdinic.blogovi.model.BlogPost> findBlogPostsByApprovedNot(Boolean approved) {
        if (approved == null)
            throw new IllegalArgumentException("The approved argument is required");
        EntityManager em = entityManager();
        TypedQuery<BlogPost> q = em.createQuery(
                "SELECT o FROM BlogPost AS o WHERE o.approved IS NOT :approved ORDER BY o.id DESC", BlogPost.class);
        q.setParameter("approved", approved);
        return q;
    }

    public static List<com.mdinic.blogovi.model.BlogPost> findBlogPostsByPersonApprovedNot(Person person,
            Boolean approved, int firstResult, int maxResults) {
        if (approved == null)
            throw new IllegalArgumentException("The approved argument is required");
        EntityManager em = entityManager();
        TypedQuery<BlogPost> q = em
                .createQuery(
                        "SELECT o FROM BlogPost AS o WHERE o.approved IS NOT :approved AND o.person = :person ORDER BY o.id DESC",
                        BlogPost.class).setFirstResult(firstResult).setMaxResults(maxResults);
        q.setParameter("approved", approved);
        q.setParameter("person", person);
        return q.getResultList();
    }

    public static TypedQuery<com.mdinic.blogovi.model.BlogPost> findBlogPostsByDescriptionEquals(String description) {
        if (description == null || description.length() == 0)
            throw new IllegalArgumentException("The description argument is required");
        EntityManager em = entityManager();
        TypedQuery<BlogPost> q = em.createQuery("SELECT o FROM BlogPost AS o WHERE o.description = :description",
                BlogPost.class);
        q.setParameter("description", description);
        return q;
    }

    public static TypedQuery<com.mdinic.blogovi.model.BlogPost> findBlogPostsByHeadlineEquals(String headline) {
        if (headline == null || headline.length() == 0)
            throw new IllegalArgumentException("The headline argument is required");
        EntityManager em = entityManager();
        TypedQuery<BlogPost> q = em.createQuery("SELECT o FROM BlogPost AS o WHERE o.headline = :headline",
                BlogPost.class);
        q.setParameter("headline", headline);
        return q;
    }

    public static TypedQuery<com.mdinic.blogovi.model.BlogPost> findBlogPostsByUrlEquals(String url) {
        if (url == null || url.length() == 0)
            throw new IllegalArgumentException("The url argument is required");
        EntityManager em = entityManager();
        TypedQuery<BlogPost> q = em.createQuery("SELECT o FROM BlogPost AS o WHERE o.url = :url", BlogPost.class);
        q.setParameter("url", url);
        return q;
    }

    public static TypedQuery<com.mdinic.blogovi.model.BlogPost> findBlogPostsByBlog(Blog blog) {
        if (blog == null)
            throw new IllegalArgumentException("The blog argument is required");
        EntityManager em = entityManager();
        TypedQuery<BlogPost> q = em.createQuery("SELECT o FROM BlogPost AS o WHERE o.blog = :blog ORDER BY o.id DESC",
                BlogPost.class);
        q.setParameter("blog", blog);
        return q;
    }

    @Transactional
    public void persist() {
        if (this.entityManager == null)
            this.entityManager = entityManager();
        this.entityManager.persist(this);
    }

    @Transactional
    public void remove() {
        if (this.entityManager == null)
            this.entityManager = entityManager();
        if (this.entityManager.contains(this)) {
            this.entityManager.remove(this);
        } else {
            BlogPost attached = findBlogPost(this.id);
            this.entityManager.remove(attached);
        }
    }

    @Transactional
    public void flush() {
        if (this.entityManager == null)
            this.entityManager = entityManager();
        this.entityManager.flush();
    }

    @Transactional
    public void clear() {
        if (this.entityManager == null)
            this.entityManager = entityManager();
        this.entityManager.clear();
    }

    @Transactional
    public com.mdinic.blogovi.model.BlogPost merge() {
        if (this.entityManager == null)
            this.entityManager = entityManager();
        BlogPost merged = this.entityManager.merge(this);
        this.entityManager.flush();
        return merged;
    }

    @Override
    public String toString() {
        return "[" + posted + "] " + headline + " by " + person;
    }

    public static TypedQuery<com.mdinic.blogovi.model.BlogPost> findBlogPostsByTitleSetNot(Boolean titleSet) {
        if (titleSet == null)
            throw new IllegalArgumentException("The titleSet argument is required");
        EntityManager em = entityManager();
        TypedQuery<BlogPost> q = em.createQuery("SELECT o FROM BlogPost AS o WHERE o.titleSet IS NOT :titleSet",
                BlogPost.class);
        q.setParameter("titleSet", titleSet);
        return q;
    }

    public static List<com.mdinic.blogovi.model.BlogPost> findBlogPostsByPersonEntries(Person person, int firstResult,
            int maxResults) {
        if (person == null)
            throw new IllegalArgumentException("The person argument is required");
        EntityManager em = entityManager();
        TypedQuery<BlogPost> q = em
                .createQuery("SELECT o FROM BlogPost AS o WHERE o.person = :person ORDER BY o.id DESC", BlogPost.class)
                .setFirstResult(firstResult).setMaxResults(maxResults);
        q.setParameter("person", person);
        return q.getResultList();
    }

    public static TypedQuery<com.mdinic.blogovi.model.BlogPost> findBlogPostsByPersonAndFav(Person person) {
        if (person == null)
            throw new IllegalArgumentException("The person argument is required");
        EntityManager em = entityManager();
        TypedQuery<BlogPost> q = em.createQuery(
                "SELECT o FROM BlogPost AS o, Fav as f WHERE o.person = :person AND f.person = :person AND f.post = o",
                BlogPost.class);
        q.setParameter("person", person);
        return q;
    }

    @Transactional
    public int incComment() {
        EntityManager em = entityManager();
        Query createQuery = em.createQuery("UPDATE BlogPost o SET o.comment = o.comment + 1 WHERE o.id = :id");
        createQuery.setParameter("id", id);
        return createQuery.executeUpdate();
    }

    @Transactional
    public int incView() {
        EntityManager em = entityManager();
        Query createQuery = em.createQuery("UPDATE BlogPost o SET o.view = o.view + 1 WHERE o.id = :id");
        createQuery.setParameter("id", id);
        return createQuery.executeUpdate();
    }

    @Transactional
    public int incClick() {
        EntityManager em = entityManager();
        Query createQuery = em.createQuery("UPDATE BlogPost o SET o.click = o.click + 1 WHERE o.id = :id");
        createQuery.setParameter("id", id);
        return createQuery.executeUpdate();
    }

    public static long countVotesUpByPerson(Person person) {
        TypedQuery<Long> createQuery = entityManager().createQuery(
                "SELECT COUNT(o) FROM BlogPost o, Vote as v WHERE o = v.post AND v.up = true AND o.person = :person",
                Long.class);
        createQuery.setParameter("person", person);
        return createQuery.getSingleResult();
    }

    public static long countVotesDownByPerson(Person person) {
        TypedQuery<Long> createQuery = entityManager().createQuery(
                "SELECT COUNT(o) FROM BlogPost o, Vote as v WHERE o = v.post AND v.up = false AND o.person = :person",
                Long.class);
        createQuery.setParameter("person", person);
        return createQuery.getSingleResult();
    }

    public static List<com.mdinic.blogovi.model.BlogPost> findBlogPostsByBlog(Blog blog, int firstResult, int maxResults) {
        if (blog == null)
            throw new IllegalArgumentException("The blog argument is required");
        EntityManager em = entityManager();
        TypedQuery<BlogPost> q = em
                .createQuery("SELECT o FROM BlogPost AS o WHERE o.blog = :blog ORDER BY o.id DESC", BlogPost.class)
                .setFirstResult(firstResult).setMaxResults(maxResults);
        q.setParameter("blog", blog);
        return q.getResultList();
    }

    public static List<com.mdinic.blogovi.model.BlogPost> findBlogPostsByPersonAndFavEntries(Person person,
            int firstResult, int maxResults) {
        if (person == null)
            throw new IllegalArgumentException("The person argument is required");
        EntityManager em = entityManager();
        TypedQuery<BlogPost> q = em
                .createQuery(
                        "SELECT o FROM BlogPost AS o, Fav as f WHERE o.person = :person AND f.person = :person AND f.post = o",
                        BlogPost.class).setFirstResult(firstResult).setMaxResults(maxResults);
        q.setParameter("person", person);
        return q.getResultList();
    }

    public static TypedQuery<com.mdinic.blogovi.model.BlogPost> findBlogPostsByPerson(Person person) {
        if (person == null)
            throw new IllegalArgumentException("The person argument is required");
        EntityManager em = entityManager();
        TypedQuery<BlogPost> q = em.createQuery("SELECT o FROM BlogPost AS o WHERE o.person = :person", BlogPost.class);
        q.setParameter("person", person);
        return q;
    }

    public static TypedQuery<BlogPost> findBlogPostsByPromotedIsNotNull() {
        EntityManager em = entityManager();
        TypedQuery<BlogPost> q = em.createQuery("SELECT o FROM BlogPost AS o WHERE o.promoted IS NOT NULL",
                BlogPost.class);
        return q;
    }

    public static TypedQuery<BlogPost> findBlogPostsByPromotedIsNull() {
        EntityManager em = entityManager();
        TypedQuery<BlogPost> q = em.createQuery("SELECT o FROM BlogPost AS o WHERE o.promoted IS NULL", BlogPost.class);
        return q;
    }

    @SuppressWarnings("unchecked")
    public static List<BlogPost> findBlogPostsByFollowersRead(Person person, Person follow, int firstResult,
            int maxResults) {
        EntityManager em = entityManager();
        StringBuilder sb = new StringBuilder();
        sb.append("SELECT DISTINCT blog_post.*                         ");
        sb.append("   FROM blog_post ");
        sb.append("   JOIN share ON share.blog_post=blog_post.id ");
        sb.append("   JOIN follow ON follow.follows=share.person ");
        sb.append("JOIN read_share ON read_share.blog_post = blog_post.id ");
        sb.append("        AND follow.person=                   " + person.getId());
        if (follow != null) {
            sb.append("        AND follow.follows=                   " + follow.getId());
        }
        Query createQuery = em.createNativeQuery(sb.toString(), BlogPost.class);
        createQuery.setFirstResult(firstResult);
        createQuery.setMaxResults(maxResults);
        if (LOG.isDebugEnabled()) {
            LOG.debug(sb.toString());
        }
        return createQuery.getResultList();
    }

    @SuppressWarnings("unchecked")
    public static List<BlogPost> findBlogPostsByFollowersUnread(Person person, Person follow, int firstResult,
            int maxResults) {

        EntityManager em = entityManager();
        StringBuilder sb = new StringBuilder();
        sb.append("SELECT DISTINCT blog_post.*                         ");
        sb.append("   FROM blog_post ");
        sb.append("   JOIN share ON share.blog_post=blog_post.id ");
        sb.append("   JOIN follow ON follow.follows=share.person ");
        sb.append(" WHERE follow.person= " + person.getId());
        sb.append(" AND follow.person=" + person.getId());
        sb.append(" AND blog_post.id NOT IN (SELECT read_share.blog_post FROM read_share WHERE read_share.person="
                + person.getId());
        sb.append(")");

        if (follow != null) {
            sb.append("        AND follow.follows=                   " + follow.getId());
        }
        Query createQuery = em.createNativeQuery(sb.toString(), BlogPost.class);
        createQuery.setFirstResult(firstResult);
        createQuery.setMaxResults(maxResults);
        if (LOG.isDebugEnabled()) {
            LOG.debug(sb.toString());
        }
        return createQuery.getResultList();
    }

    @Transactional
    public void promoteWithThisCondition(String sql) {
        if (StringUtils.isBlank(sql)) {
            return;
        }
        EntityManager em = entityManager();
        StringBuilder sb = new StringBuilder();
        sb.append("UPDATE blogovi.blog_post SET blog_post.promoted = now() WHERE ");
        sb.append("blog_post.promoted IS NULL AND blog_post.approved = true ");
        sb.append(sql);
        Query createQuery = em.createNativeQuery(sb.toString());
        createQuery.executeUpdate();
    }

    @SuppressWarnings("unchecked")
    public static List<BlogPost> findSharedBlogPostsByPerson(Person person, int firstResult, int maxResults) {
        EntityManager em = entityManager();
        StringBuilder sb = new StringBuilder();
        sb.append("SELECT DISTINCT blog_post.*                         ");
        sb.append("   FROM blog_post ");
        sb.append("   JOIN share ON share.blog_post=blog_post.id ");
        sb.append(" WHERE share.person= " + person.getId());

        Query createQuery = em.createNativeQuery(sb.toString(), BlogPost.class);
        createQuery.setFirstResult(firstResult);
        createQuery.setMaxResults(maxResults);
        if (LOG.isDebugEnabled()) {
            LOG.debug(sb.toString());
        }
        return createQuery.getResultList();
    }

}
